package com.xksms.miniotest.utils;

import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.apache.commons.compress.utils.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author ChengChao
 * @Description minio工具类
 * @Date 8:43 2022/8/3
 **/
@Component
public class MinioUtil {
    @Autowired
    private MinioClient minioClient1;
    @Value("${minio.url}")
    private String ip1;
    private static MinioClient minioClient;
    private static String ip;


    @PostConstruct
    public void init() {
        minioClient = minioClient1;
        ip = ip1;
    }

    /**
     * @param bucket 桶名
     * @Author chengchao
     * @Description 创建一个桶，并且桶被公开
     * @Date 8:43 2022/8/3
     **/
    public static void createBucket(String bucket) throws Exception {
        boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
        if (!found) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
        }
        // 设置桶的访问权限为公开
        setConfigByBucket(bucket);
    }

    /**
     * @param stream     文件流
     * @param bucketName     桶名
     * @param objectName 路径
     * @Author chengchao
     * @Description 上传一个文件
     * @Date 8:43 2022/8/3
     **/
    public static void uploadFile(InputStream stream, String bucketName, String objectName) throws Exception {
        createBucket(bucketName);
        try {
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName)
                    .stream(stream, -1, 10485760).build());
        } finally {
            stream.close();
        }
    }

    public static String uploadFile(MultipartFile fileInfo, String bucketName, String path, String rename) throws Exception {
        try (InputStream inputStream = fileInfo.getInputStream()) {
            createBucket(bucketName);
            List<SnowballObject> objects = new ArrayList<>();
            path = disposePath(path, rename, fileInfo.getOriginalFilename());
            objects.add(new SnowballObject(path, inputStream, fileInfo.getSize(), null));
            String url = getImageUrl(bucketName, path);
            minioClient.uploadSnowballObjects(
                    UploadSnowballObjectsArgs.builder().bucket(bucketName).objects(objects).build());
            return url;
        }
    }

    public static String uploadFile(File fileInfo, String bucketName, String path, String rename) throws Exception {
        createBucket(bucketName);
        List<SnowballObject> objects = new ArrayList<>();
        path = disposePath(path, rename, fileInfo.getName());
        try (InputStream inputStream = Files.newInputStream(fileInfo.toPath())) {
            objects.add(new SnowballObject(path, inputStream, fileInfo.length(), null));
            String url = getImageUrl(bucketName, path);
            minioClient.uploadSnowballObjects(
                    UploadSnowballObjectsArgs.builder().bucket(bucketName).objects(objects).build());
            return url;
        }
    }

    /**
     * @param path     路径
     * @param rename   重命名
     * @param fileName 准备重命名的文件名
     * @return java.lang.String
     * @Author chengchao
     * @Description 处理路径
     * @Date 10:57 2022/8/4
     **/
    private static String disposePath(String path, String rename, String fileName) {
        char suffix = '/';
        //如果最后一个字符是/，则去掉
        if (StringUtils.hasText(path) && suffix == path.charAt(path.length() - 1)) {
            path = path.substring(0, path.length() - 1);
        }
        path = ((StringUtils.hasText(path) ? (path + "/") : "")) + (rename == null ? fileName : rename);
        return path;
    }

    /**
     * @param bucketName 桶名
     * @param path       路径
     * @return java.lang.String
     * @Author chengchao
     * @Description 获取上传后的图片url
     * @Date 11:14 2022/8/4
     **/
    public static String getImageUrl(String bucketName, String path) {
        return ip + "/" + bucketName + "/" + path;
    }

    /**
     * @param fileinfos  为了存在路径  文件名前面可以加/ 加路径
     * @param bucketName 桶名
     * @param path       路径
     * @Author chengchao
     * @Description
     * @Date 8:44 2022/8/3
     **/
    public static List<String> uploadFiles(MultipartFile[] fileinfos, String bucketName, String path) throws Exception {
        //数组转为list
        List<MultipartFile> fileList = new LinkedList<>();
        Collections.addAll(fileList, fileinfos);
        return uploadFiles(fileList, bucketName, path);
    }

    public static List<String> uploadFiles(List<MultipartFile> fileinfos, String bucketName, String path) throws Exception {
        createBucket(bucketName);
        List<SnowballObject> objects = new ArrayList<>();
        List<String> urls = new ArrayList<>();
        for (MultipartFile fileinfo : fileinfos) {
            String path1 = disposePath(path, null, fileinfo.getOriginalFilename());
            try (InputStream inputStream = fileinfo.getInputStream()) {
                objects.add(new SnowballObject(path1, inputStream, fileinfo.getSize(), null));
                urls.add(getImageUrl(bucketName, path1));
            }
        }
        minioClient.uploadSnowballObjects(
                UploadSnowballObjectsArgs.builder().bucket(bucketName).objects(objects).build());

        return urls;
    }

    /**
     * description:   多文件上传同时支持重命名
     * @param fileinfos 文件
     * @param bucketName 桶名
     * @param path 路径
     * @param rename  重命名
     * @return java.util.List<java.lang.String>
     * @throws Exception exception
     * @author ChengChao
     * @date 2022/8/9 17:22
     */
    public static List<String> uploadFiles(MultipartFile[] fileinfos, String bucketName, String path, String rename) throws Exception {
        //数组转为list
        List<MultipartFile> fileList = new LinkedList<>();
        Collections.addAll(fileList, fileinfos);
        return uploadFiles(fileList, bucketName, path, rename);
    }

    public static List<String> uploadFiles(List<MultipartFile> fileinfos, String bucketName, String path, String rename) throws Exception {
        createBucket(bucketName);
        //解决名称重复的问题
        int i = 1;
        //获得文件名和后缀
        String[] split = rename.split("\\.");
        List<SnowballObject> objects = new ArrayList<>();
        List<String> urls = new ArrayList<>();
        for (MultipartFile fileinfo : fileinfos) {
            String path1 = disposePath(path, split[0] + "(" + (i++) + ")." + split[1], fileinfo.getOriginalFilename());
            try (InputStream inputStream = fileinfo.getInputStream()) {
                objects.add(new SnowballObject(path1, inputStream, fileinfo.getSize(), null));
                urls.add(getImageUrl(bucketName, path1));
            }
        }
        minioClient.uploadSnowballObjects(
                UploadSnowballObjectsArgs.builder().bucket(bucketName).objects(objects).build());

        return urls;
    }


    /**
     * @param bucket     桶名
     * @param objectName 路径
     * @param duration   时长 最小1秒 最大7天
     * @param timeUnit   时间单位
     * @return java.lang.String
     * @Author chengchao
     * @Description 获取分享链接
     * @Date 8:53 2022/8/3
     **/
    public static String getShareUrl(String bucket, String objectName, int duration, TimeUnit timeUnit) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucket)
                        .object(objectName)
                        .expiry(duration, timeUnit)
                        .build());
    }

    /**
     * @return java.util.List<java.lang.String>
     * @Author chengchao
     * @Description 列出所有的桶
     * @Date 8:45 2022/8/3
     **/
    public static List<String> listBuckets() throws Exception {
        List<Bucket> list = minioClient.listBuckets();
        List<String> names = new ArrayList<>();
        list.forEach(b -> names.add(b.name()));
        return names;
    }


    /**
     * @param bucket     桶名
     * @param objectName 路径及文件名     /xxx/xxx/xxx.png
     * @return java.io.InputStream
     * @Author chengchao
     * @Description 下载一个文件 给一个inputStream
     * @Date 8:45 2022/8/3
     **/
    public static InputStream download(String bucket, String objectName) throws Exception {
        return minioClient.getObject(
                GetObjectArgs.builder().bucket(bucket).object(objectName).build());
    }

    public static InputStream download(String url) throws Exception {
        HashMap<String, String> bucketAndObjectName = getBucketAndObjectName(url);
        return download(bucketAndObjectName.get("bucket"), bucketAndObjectName.get("objectName"));
    }

    /**
     * @description 下载到浏览器
     * @param url 网址
     * @param response 回答
     * @throws Exception 异常
     * @author 程超
     * @date 2023/01/06 11:36:39
     */
    public static void download(String url, HttpServletResponse response) throws Exception {
        try (InputStream download = MinioUtil.download(url)) {
            StatObjectResponse statObjectResponse = MinioUtil.statObject(url);
            HashMap<String, String> bucketAndObjectName = getBucketAndObjectName(url);

            String contentDisposition = "attachment;filename=" + bucketAndObjectName.get("objectName");
            // 设置头
            response.setContentType(statObjectResponse.contentType());
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition", contentDisposition);
            // 获取绑定了客户端的流 注意 ServletOutputStream不要手动关闭
            ServletOutputStream output = response.getOutputStream();
            // 把输入流中的数据写入到输出流中
            IOUtils.copy(download, output);
        }
    }

    /**
     * description:  下载一个目录下的所有文件
     *
     * @param bucket 桶名
     * @return java.util.List<java.io.InputStream>
     * @throws Exception exception
     * @author ChengChao
     * @date 2022/8/5 16:14
     */
    public static List<InputStream> downloadAll(String bucket) throws Exception {
        boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
        if (found) {
            // 列出'my-bucketname'里的对象
            Iterable<Result<Item>> myObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).build());

            for (Result<Item> result : myObjects) {
                Item item = result.get();
                System.out.println(item.lastModified() + ", " + item.size() + ", " + item.objectName());

                minioClient.downloadObject(
                        DownloadObjectArgs.builder()
                                .bucket(bucket)
                                .object(item.objectName())
                                .filename(item.objectName())
                                .build());
            }
        }
        return null;
    }

    /**
     * @param bucket 桶名
     * @Author chengchao
     * @Description 删除桶
     * @Date 8:46 2022/8/3
     **/
    public static void deleteBucket(String bucket) throws Exception {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucket).build());
    }

    /**
     * @param bucket     桶名
     * @param objectName 路径及文件名     /xxx/xxx/xxx.png
     * @Author chengchao
     * @Description 删除一个对象
     * @Date 8:47 2022/8/3
     **/
    public static void deleteObject(String bucket, String objectName) throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectName).build());
    }

    public static void deleteObject(String url) throws Exception {
        HashMap<String, String> bucketAndObjectName = getBucketAndObjectName(url);
        deleteObject(bucketAndObjectName.get("bucket"), bucketAndObjectName.get("objectName"));
    }

    /**
     * description:  删除多个对象
     * @param bucket 桶名
     * @param objectNames  路径及文件名     /xxx/xxx/xxx.png
     * @return java.lang.Iterable<io.minio.Result < io.minio.messages.DeleteError>>
     * @author ChengChao
     * @date 2022/8/5 17:03
     */
    public static Iterable<Result<DeleteError>> deleteObjects(String bucket, List<String> objectNames) {
        List<DeleteObject> objects = new LinkedList<>();
        objectNames.forEach(objectName -> objects.add(new DeleteObject(objectName)));
        return minioClient.removeObjects(
                RemoveObjectsArgs.builder().bucket(bucket).objects(objects).build());
    }

    public static Iterable<Result<DeleteError>> deleteObjects(List<String> urls) {
        ArrayList<String> objectNames = new ArrayList<>();
        urls.forEach(url -> objectNames.add(getBucketAndObjectName(url).get("objectName")));
        return deleteObjects(getBucketAndObjectName(urls.get(0)).get("bucket"), objectNames);
    }

    public static Iterable<Result<DeleteError>> deleteObjects(String[] urls) {
        ArrayList<String> objectNames = new ArrayList<>();
        for (String url : urls) {
            objectNames.add(getBucketAndObjectName(url).get("objectName"));
        }
        return deleteObjects(getBucketAndObjectName(urls[0]).get("bucket"), objectNames);
    }


    /**
     * description:  获取对象信息和元数据。
     *
     * @param bucket     桶名
     * @param objectName 路径及文件名     /xxx/xxx/xxx.png
     * @return io.minio.StatObjectResponse
     * @author chengchao
     * @date 2022/8/5 14:34
     */
    public static StatObjectResponse statObject(String bucket, String objectName) throws Exception {
        return minioClient.statObject(
                StatObjectArgs.builder().bucket(bucket).object(objectName).build());
    }

    public static StatObjectResponse statObject(String url) throws Exception {
        HashMap<String, String> bucketAndObjectName = getBucketAndObjectName(url);
        return statObject(bucketAndObjectName.get("bucket"), bucketAndObjectName.get("objectName"));
    }

    /**
     * description:  获取桶的配置信息
     *
     * @param bucket 桶名
     * @return io.minio.StatBucketResponse
     * @author chengchao
     * @date 2022/8/5 14:34
     */
    public static String getConfigByBucket(String bucket) throws Exception {
        return minioClient.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucket).build());
    }

    //将桶改为public
    public static void setConfigByBucket(String bucket) throws Exception {
        /**
         * bucket权限-只读
         */
        final String WRITE_ONLY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + bucket + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::" + bucket + "/*\"]}]}";
        /**
         * bucket权限-读写
         */
        final String READ_WRITE = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + bucket + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::" + bucket + "/*\"]}]}";


        minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).config(READ_WRITE).build());
    }


    /**
     * description:   根据url获得桶名和objectName
     *
     * @param url url
     * @return java.util.HashMap<java.lang.String, java.lang.String>
     * @author ChengChao
     * @date 2022/8/5 16:13
     */
    public static HashMap<String, String> getBucketAndObjectName(String url) {
        String[] split = url.split("/");
        String bucket = split[3];
        String objectName = url.split((bucket + "/"))[1];
        HashMap<String, String> map = new HashMap<>();
        map.put("bucket", bucket);
        map.put("objectName", objectName);
        return map;
    }
}
